﻿using NCS.IConnect.Core.Utilities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text;
using System.Web.Http.Controllers;

namespace NCS.IConnect.Core.WebApi.Description
{
    /// <summary>
    /// The response specific descriptor.
    /// </summary>
    public class ResponseDescriptor
    {
        /// <summary>
        /// Gets <see cref="ApiActionDescriptor"/>.
        /// </summary>
        /// <value>The <see cref="ApiActionDescriptor"/>.</value>
        [JsonIgnore]
        public ApiActionDescriptor ApiActionDescriptor { get; private set; }

        /// <summary>
        /// Gets the condition.
        /// </summary>
        /// <value>The condition.</value>
        public ConditionInfo Condition { get; private set; }

        /// <summary>
        /// Gets the response status code.
        /// </summary>
        /// <value>The response status code.</value>
        public HttpStatusCode StatusCode { get; private set; }

        /// <summary>
        /// <see cref="HttpStatusCode"/> specific Reason Phrase.
        /// </summary>
        public string ReasonPhrase
        {
            get { return this.StatusCode.GetReasonPhrase(); }
        }

        /// <summary>
        /// Gets <see cref="HttpHeaderDescriptor"/>s.
        /// </summary>
        /// <value>The <see cref="HttpHeaderDescriptor"/>s.</value>
        public HttpHeaderDescriptor[] Headers { get; private set; }

        /// <summary>
        /// Gets the <see cref="HttpContentDescriptor"/>.
        /// </summary>
        /// <value>The <see cref="HttpContentDescriptor"/>.</value>
        public HttpContentDescriptor Content { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance has content.
        /// </summary>
        /// <value><c>true</c> if this instance has content; otherwise, <c>false</c>.</value>
        public bool HasContent
        {
            get { return null != this.Content; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ResponseDescriptor"/> class.
        /// </summary>
        /// <param name="apiActionDescriptor">The <see cref="ApiActionDescriptor"/>.</param>
        /// <param name="responseAttribute">The <see cref="ResponseAttribute"/> to provider response description information.</param>
        public ResponseDescriptor( ApiActionDescriptor apiActionDescriptor, ResponseAttribute responseAttribute)
        {
            Guard.ArgumentNotNull(apiActionDescriptor, "apiActionDescriptor");
            Guard.ArgumentNotNull(responseAttribute, "responseAttribute");

            this.ApiActionDescriptor = apiActionDescriptor;
            this.Condition = responseAttribute.Condition;
            this.StatusCode = responseAttribute.StatusCode;
            this.Content = GetContentDescriptor(apiActionDescriptor, responseAttribute);
            this.Headers = this.GetHeaderDescriptors(apiActionDescriptor.HttpActionDescriptor);
        }

        /// <summary>
        /// Gets the content descriptor.
        /// </summary>
        /// <param name="apiActionDescriptor">The <see cref="ApiActionDescriptor"/>.</param>
        /// <param name="responseAttribute">The <see cref="ResponseAttribute"/>.</param>
        /// <returns>The <see cref="HttpContentDescriptor"/>.</returns>
        protected  HttpContentDescriptor GetContentDescriptor(ApiActionDescriptor apiActionDescriptor, ResponseAttribute responseAttribute)
        {
            Guard.ArgumentNotNull(apiActionDescriptor, "apiActionDescriptor");
            Guard.ArgumentNotNull(responseAttribute, "responseAttribute");

            var supportedMediaTypes = apiActionDescriptor.HttpActionDescriptor.Configuration.Formatters.SelectMany(f => f.SupportedMediaTypes).Select(m => m.MediaType);
            if (null != responseAttribute.ContentDataType && typeof(void) != responseAttribute.ContentDataType)
            {
                return new HttpContentDescriptor(responseAttribute.ContentDataType, type => ApiDescriptorGenerator.GetSample(type, apiActionDescriptor.HttpActionDescriptor), supportedMediaTypes.ToArray());
            }
            else if(!string.IsNullOrEmpty(responseAttribute.ErrorCode))
            {
                return new HttpContentDescriptor(responseAttribute.ErrorCode, supportedMediaTypes.ToArray());
            }
            return null;
        }

        /// <summary>
        /// Gets the header descriptors.
        /// </summary>
        /// <param name="httpActionDescriptor">The <see cref="HttpActionDescriptor"/>.</param>
        /// <returns>The <see cref="HttpHeaderDescriptor"/>s.</returns>
        protected  HttpHeaderDescriptor[] GetHeaderDescriptors(HttpActionDescriptor httpActionDescriptor)
        {
            Guard.ArgumentNotNull(httpActionDescriptor, "httpActionDescriptor");

            Collection<HttpHeaderDescriptor> headers = new Collection<HttpHeaderDescriptor>();
            foreach (ResponseHeaderAttribute attribute in httpActionDescriptor.GetCustomAttributes<ResponseHeaderAttribute>())
            {
                if (attribute.ConditionCode == this.Condition.Code)
                {
                    headers.Add(attribute.CreateHeaderDescriptor());
                }
            }
            return headers.ToArray();
        }
    }
}